import {
    Range,
    TextDocumentEdit,
    TextEdit,
    WorkspaceEdit
} from "vscode-languageserver";
import { RequestRenameResult, TextRange } from "../common/types";
import { t } from "./analysis";
import { TextDocuments } from "./textDocument";
import { pathToUri } from "./translation";
import { createRenameTextEdit, getNodeWithPathAtPosition } from "./util";
import { ProjectWorkspace } from "./workspace";

export async function provideRename(source: t.SourceFile, position: number, documents: TextDocuments): Promise<RequestRenameResult | null> {
    let nodeResult = getNodeWithPathAtPosition(source, position);
    let node = nodeResult.node;
    let symbol = nodeResult.node?.symbol;

    if (!node || !symbol || !node.isIdentifier()) {
        return null;
    }

    let canRename = true;
    let errorMessage: string | undefined;
    // 非本地定义类型，不允许使用重命名
    if (!symbol.is(t.SymbolKind.variable | t.SymbolKind.array | t.SymbolKind.macro | t.SymbolKind.function)) {
        canRename = false;
        errorMessage = "只能重命名本地变量、函数或宏";
    }

    let trigger: TextRange = {
        start: source.getLineAndCharactorOfPosition(node.start),
        end: source.getLineAndCharactorOfPosition(node.end),
    };
    let changes: { [uri: string]: TextRange[] } = {};

    symbol.tracker.forEachNode(n => {
        if (n === node) {
            return;
        }
        let source = n.getSourceFile();
        if (source) {
            let uri = pathToUri(source.fileName);
            let document = documents.get(uri);
            if (document) {
                (changes[uri] || (changes[uri] = [])).push({
                    start: document.positionAt(n.start),
                    end: document.positionAt(n.end),
                });
            }
        }
    });

    return {
        canRename,
        triggerLocation: trigger,
        changes: changes,
        errorMessage
    };
}

export async function provideRenameRequest(source: t.SourceFile, position: number, workspace: ProjectWorkspace, newText: string): Promise<WorkspaceEdit | null> {
    let nodeResult = getNodeWithPathAtPosition(source, position);
    let symbol = nodeResult.node?.symbol;

    if (!nodeResult.node || !symbol) {
        return null;
    }

    let changes: { [uri: string]: TextEdit[] } = {};
    let textEdits: TextDocumentEdit[] = [];

    symbol.tracker.forEachNode(node => {
        let source = node.getSourceFile();
        if (source) {
            let uri = pathToUri(source.fileName);
            let document = workspace.getTextDocument(source.fileName);
            if (document) {
                let arr: TextEdit[];
                if (changes[uri]) {
                    arr = changes[uri];
                }
                else {
                    changes[uri] = (arr = []);
                }
                arr.push(createRenameTextEdit(document, node, newText));
            }
        }
    });

    Object.keys(changes).forEach(k => {
        let change = changes[k];
        textEdits.push(TextDocumentEdit.create({ uri: k, version: null }, change));
    });

    return { changes, documentChanges: textEdits };

}

export async function providePrepareRenameRequest(source: t.SourceFile, position: number) {
    const nodeResult = getNodeWithPathAtPosition(source, position);
    const symbol = nodeResult.node?.symbol;

    if (!nodeResult.node || !symbol) {
        return undefined;
    }

    return Range.create(
        source.getLineAndCharactorOfPosition(nodeResult.node.start),
        source.getLineAndCharactorOfPosition(nodeResult.node.end)
    );
}
